Skip to content

feat(studio): Additional numerical range filters - #577

Merged
nakolean merged 2 commits into
mainfrom
nkolean/experiment-additional-range-filtering
Jul 6, 2026
Merged

feat(studio): Additional numerical range filters#577
nakolean merged 2 commits into
mainfrom
nkolean/experiment-additional-range-filtering

Conversation

@nakolean

@nakolean nakolean commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Summary

  • New Features

    • Added dynamic filter-field mapping support (including function-based mapping and pattern-based remaps).
    • Enabled numeric range filtering for evaluator average scores, average cost, and run count.
  • Bug Fixes

    • Improved backend filter key resolution so UI filter values map to the correct fields, with sensible fallback when a mapping is unavailable.
    • Kept evaluator score columns available and stably ordered even when the active filters produce zero matching rows.
  • Tests

    • Expanded coverage for the new dynamic mapping and remapping behaviors.

…rs.<name>.mean

Signed-off-by: Nicholas Kolean <nakolean@gmail.com>
@nakolean
nakolean requested review from a team as code owners July 6, 2026 18:14
@github-actions github-actions Bot added the feat label Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6871ffab-4c5b-4129-9bf6-4d392f3de63e

📥 Commits

Reviewing files that changed from the base of the PR and between 4a3962a and 68cdead.

📒 Files selected for processing (2)
  • web/packages/common/src/hooks/useStudioDataViewState/filterFieldMap.integration.test.tsx
  • web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx

📝 Walkthrough

Walkthrough

Extends useStudioDataViewState so filterFieldMap can be a function, updates ExperimentGroupDataView to use dynamic evaluator filter keys, preserves evaluator columns from active filters, and enables numeric range filtering for evaluator, cost, and run count columns.

Changes

Dynamic filterFieldMap and evaluator column filtering

Layer / File(s) Summary
filterFieldMap type and apiFilter construction
web/packages/common/src/hooks/useStudioDataViewState/index.ts
filterFieldMap now accepts a mapping function as well as a record, and apiFilter falls back to the original filter id when the function returns undefined.
filterFieldMap tests
web/packages/common/src/hooks/useStudioDataViewState/index.test.tsx, web/packages/common/src/hooks/useStudioDataViewState/filterFieldMap.integration.test.tsx
Tests cover function-form remapping, undefined fallback, dynamic evaluator key mapping, and numberRangeFilter behavior for evaluator and run count columns.
deriveEvaluatorNames utility
web/packages/studio/src/components/dataViews/ExperimentGroupDataView/util.ts, util.test.ts
Adds deriveEvaluatorNames, which unions evaluator names from row data and active evaluator filters, then sorts and deduplicates them.
ExperimentGroupDataView wiring
web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx
Uses getExperimentFilterField, switches evaluator column derivation to deriveEvaluatorNames, and adds numberRangeFilter meta to evaluator, cost, and run count columns.

Possibly related PRs

Suggested reviewers: walston, shanaiabuggy

Sequence Diagram(s)

sequenceDiagram
  participant ExperimentGroupDataView
  participant useStudioDataViewState
  participant useCustomReactTable
  participant backend API
  ExperimentGroupDataView->>useStudioDataViewState: filterFieldMap(id)
  useStudioDataViewState->>useCustomReactTable: table filter state
  useCustomReactTable->>backend API: apiFilter.filter with mapped keys
  backend API-->>ExperimentGroupDataView: filtered results
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: adding numeric range filtering support in studio data views.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch nkolean/experiment-additional-range-filtering

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
web/packages/common/src/hooks/useStudioDataViewState/index.ts (1)

484-503: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Possible silent filter collision via Object.fromEntries.

If filterFieldMap (function or record form) maps two distinct filter ids to the same key, Object.fromEntries silently drops the earlier entry — one active UI filter would be dropped without any warning. Not exploited by current callers (getExperimentFilterField maps distinct ids to distinct keys), but nothing here guards against it as the mapping function grows.

🛡️ Optional dedup guard
-      result.filter = Object.fromEntries(
-        debouncedColumnFilters
-          .filter((f) => { ... })
-          .map((f) => {
-            const mappedKey =
-              typeof filterFieldMap === 'function' ? filterFieldMap(f.id) : filterFieldMap?.[f.id];
-            return [mappedKey ?? f.id, f.value];
-          })
-      ) as Partial<FilterType>;
+      const entries = debouncedColumnFilters
+        .filter((f) => { ... })
+        .map((f) => {
+          const mappedKey =
+            typeof filterFieldMap === 'function' ? filterFieldMap(f.id) : filterFieldMap?.[f.id];
+          return [mappedKey ?? f.id, f.value] as const;
+        });
+      if (process.env.NODE_ENV !== 'production') {
+        const seen = new Set<string>();
+        for (const [key] of entries) {
+          if (seen.has(key)) console.warn(`apiFilter: duplicate mapped key "${key}" — a filter was dropped`);
+          seen.add(key);
+        }
+      }
+      result.filter = Object.fromEntries(entries) as Partial<FilterType>;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/packages/common/src/hooks/useStudioDataViewState/index.ts` around lines
484 - 503, The filter assembly in useStudioDataViewState is vulnerable to silent
collisions when filterFieldMap returns the same mapped key for multiple
debouncedColumnFilters entries, because Object.fromEntries will overwrite
earlier values. Update the filter-building logic around debouncedColumnFilters
to detect duplicate mapped keys before constructing result.filter, and either
skip duplicates deterministically or surface a clear warning/error when
filterFieldMap (function or record form) maps multiple ids to the same key.
web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx (1)

48-56: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate evaluator-<name> regex — extract a shared helper.

The same /^evaluator-(.+)$/ pattern is duplicated in deriveEvaluatorNames (util.ts) and here. Two independent copies of the evaluator-id convention risk silent drift if the naming scheme ever changes.

Proposed refactor
+// util.ts
+export const parseEvaluatorId = (id: string): string | undefined =>
+  id.match(/^evaluator-(.+)$/)?.[1];
-  const evaluatorMatch = id.match(/^evaluator-(.+)$/);
-  if (evaluatorMatch) return `evaluators.${evaluatorMatch[1]}.mean`;
+  const evaluatorName = parseEvaluatorId(id);
+  if (evaluatorName) return `evaluators.${evaluatorName}.mean`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx`
around lines 48 - 56, The evaluator-id regex is duplicated between
getExperimentFilterField and deriveEvaluatorNames, so extract the
/^evaluator-(.+)$/ parsing into a shared helper and reuse it from both places.
Update the helper to return the evaluator name/field mapping once, then have
ExperimentGroupDataView and util.ts call that shared function instead of
maintaining separate copies.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@web/packages/common/src/hooks/useStudioDataViewState/index.ts`:
- Around line 484-503: The filter assembly in useStudioDataViewState is
vulnerable to silent collisions when filterFieldMap returns the same mapped key
for multiple debouncedColumnFilters entries, because Object.fromEntries will
overwrite earlier values. Update the filter-building logic around
debouncedColumnFilters to detect duplicate mapped keys before constructing
result.filter, and either skip duplicates deterministically or surface a clear
warning/error when filterFieldMap (function or record form) maps multiple ids to
the same key.

In
`@web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx`:
- Around line 48-56: The evaluator-id regex is duplicated between
getExperimentFilterField and deriveEvaluatorNames, so extract the
/^evaluator-(.+)$/ parsing into a shared helper and reuse it from both places.
Update the helper to return the evaluator name/field mapping once, then have
ExperimentGroupDataView and util.ts call that shared function instead of
maintaining separate copies.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2d08a96d-b7c2-4160-8f56-83b7943b4e74

📥 Commits

Reviewing files that changed from the base of the PR and between fb50b86 and 4a3962a.

📒 Files selected for processing (6)
  • web/packages/common/src/hooks/useStudioDataViewState/filterFieldMap.integration.test.tsx
  • web/packages/common/src/hooks/useStudioDataViewState/index.test.tsx
  • web/packages/common/src/hooks/useStudioDataViewState/index.ts
  • web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx
  • web/packages/studio/src/components/dataViews/ExperimentGroupDataView/util.test.ts
  • web/packages/studio/src/components/dataViews/ExperimentGroupDataView/util.ts

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 23224/30388 76.4% 61.2%
Integration Tests 13570/29068 46.7% 19.9%

@shanaiabuggy shanaiabuggy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful! Can we include Run Count in the set of filters as well?

Signed-off-by: Nicholas Kolean <nakolean@gmail.com>
@nakolean
nakolean added this pull request to the merge queue Jul 6, 2026
Merged via the queue into main with commit 954c403 Jul 6, 2026
55 checks passed
@nakolean
nakolean deleted the nkolean/experiment-additional-range-filtering branch July 6, 2026 21:02
arpitsardhana pushed a commit that referenced this pull request Jul 9, 2026
* feat(studio): Additional numerical range filter for cost and evaluators.<name>.mean

Signed-off-by: Nicholas Kolean <nakolean@gmail.com>

* include run count

Signed-off-by: Nicholas Kolean <nakolean@gmail.com>

---------

Signed-off-by: Nicholas Kolean <nakolean@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants